# Concave
# Copyright (C) 2012 Daniel Malmqvist
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import random
import sys
import constants
import g
#import helperfunctions


"""
http://roguebasin.roguelikedevelopment.org/index.php/Dungeon-Building_Algorithm

In this algorithm a "feature" is taken to mean any kind of map component e.g. large room, small room, corridor, circular arena, vault etc.

1. Fill the whole map with solid earth
2. Dig out a single room in the centre of the map
3. Pick a wall of any room
4. Decide upon a new feature to build
5. See if there is room to add the new feature through the chosen wall
6. If yes, continue. If no, go back to step 3
7. Add the feature through the chosen wall
8. Go back to step 3, until the dungeon is complete
9. Add the up and down staircases at random points in map
10. Finally, sprinkle some monsters and items liberally over dungeon
"""


"""
0 = nothing
1 = walkable
2 = wall
3 = enemy
4 = start
5 = end
"""




class Room():
    def __init__(self, startx, starty, stopx, stopy):
        self.startx = startx
        self.starty = starty
        self.stopx = stopx
        self.stopy = stopy


class MapMaker():
    random.seed()
    
    def generateMap(self, maxx, maxy, rooms, roomMaxX, roomMaxY):
        self.maxx = maxx
        self.maxy = maxy
        
        self.roomMaxX = roomMaxX
        self.roomMaxY = roomMaxY
        
        self.table= [ [ 0 for i in range(maxx) ] for j in range(maxy) ]
        
        
        # Size of first room (needs to be able to have a stair)
        sizex = random.randint(3,5)
        sizey = random.randint(3,5)
        
        #Place first room in the middle
        startx = (maxx-sizex)/2
        starty = (maxy-sizey)/2

        #Build room 1                
        self.buildAt(startx, starty, startx+sizex*2, starty+sizey*2, False)
        self.rooms = []
        self.rooms.append(Room(startx, starty, startx+sizex*2, starty+sizey*2))
        
        #Add stairs up to the first room
        self.addExtra(self.rooms[0], 4)
        
        # Main loop for creating new rooms
        # To not get stuck in infinite loop break at 100k tries
        counter = 0
        while len(self.rooms) < rooms and counter < 100000:
            counter += 1

            # Pick a random room
            randomRoom = self.rooms[random.randint(0, len(self.rooms) - 1)]
            
            # Shuffle direction to next room
            nextDirection = [constants.NORTH,constants.EAST,constants.SOUTH,constants.WEST]
            random.shuffle(nextDirection)

            # Try the first direction
            while (len(nextDirection) > 0):
                if (nextDirection[0] == constants.NORTH):
                    doory = randomRoom.starty-1
                    doorx = random.randint(randomRoom.startx+1, randomRoom.stopx - 1)
                    
                    if self.createRoom(doorx, doory, nextDirection[0]):
                        self.table[doory][doorx] = 1
                        self.table[doory+1][doorx] = 1
                        nextDirection = []
                        
                elif (nextDirection[0] == constants.SOUTH):
                    doory = randomRoom.stopy+1
                    doorx = random.randint(randomRoom.startx+1, randomRoom.stopx - 1)
                    
                    if self.createRoom(doorx, doory, nextDirection[0]):
                        self.table[doory][doorx] = 1
                        self.table[doory-1][doorx] = 1
                        nextDirection = []
                        
                elif (nextDirection[0] == constants.WEST):
                    doory = random.randint(randomRoom.starty+1, randomRoom.stopy - 1)
                    doorx = randomRoom.startx-1
                    
                    if self.createRoom(doorx, doory, nextDirection[0]):
                        self.table[doory][doorx] = 1
                        self.table[doory][doorx+1] = 1
                        nextDirection = []
                        
                elif (nextDirection[0] == constants.EAST):
                    doory = random.randint(randomRoom.starty+1, randomRoom.stopy - 1)
                    doorx = randomRoom.stopx+1
                    
                    if self.createRoom(doorx, doory, nextDirection[0]):
                        self.table[doory][doorx] = 1
                        self.table[doory][doorx-1] = 1
                        nextDirection = []
                if (len(nextDirection) > 0):
                    nextDirection = nextDirection[1:]
                    
        
        # Add stairs down in the last room
        # TODO: Fix bug here
        stairsDownAdded = False
        counter = 1
        while not stairsDownAdded:
            if self.addExtra(self.rooms[len(self.rooms)-counter], 5):
                stairsDownAdded = True
            else:
                counter += 1
                
        #self.printmap()
        
        #self.makeRoomsRounder()
        self.shrinkMap()
        return self.table
    def printmap(self):
        for row in self.table:
            for e in row:
                if e == 0:
                    sys.stdout.write(" ")
                elif e == 1:
                    sys.stdout.write(" ")
                elif e == 3:
                    sys.stdout.write("e")
                elif e == 4:
                    sys.stdout.write("s")
                elif e == 5:
                    sys.stdout.write("f")
                else:
                    sys.stdout.write("#")
            sys.stdout.write("\n")

    def createRoom(self, doorx, doory, direction):
        # Trys to build a room in the direction from another room
        # Returns True if build was successful
        xleft = random.randint(0,self.roomMaxX)
        xright = random.randint(0,self.roomMaxX)
        yup = random.randint(0,self.roomMaxY)
        ydown = random.randint(0,self.roomMaxY)
        
        
        if direction == constants.NORTH:
            if self.tryToBuildAt(doorx-xleft-1, doory-max(2, yup+ydown), doorx+xright+1, doory):
                return True
        elif direction == constants.SOUTH:
            if self.tryToBuildAt(doorx-xleft-1, doory, doorx+xright+1, doory+max(2, ydown+yup)):
                return True
        elif direction == constants.WEST:
            if self.tryToBuildAt(doorx-max(2,xleft+xright), doory-max(1,yup), doorx, doory+max(1,ydown)):
                return True
        elif direction == constants.EAST:
            if self.tryToBuildAt(doorx, doory-max(1,yup), doorx+max(2,xleft+xright), doory+max(1,ydown)):
                return True
    
        
    def tryToBuildAt(self, startx, starty, stopx, stopy):
        """
        Checks if this portion is only filed with 0 (nothing placed)
        
        If nothing is placed it will call buildAt
        """
        if (startx < 0 or stopx >= self.maxx):
            return False
        
        if (stopy >= self.maxy or starty < 0):
            return False
        
        if (stopy < starty or stopx < startx):
            return False
        
        
        for x in range(startx, stopx+1):
            for y in range(starty, stopy+1):
                if (self.table[y][x] != 0):
                    return False
                
        self.buildAt(startx, starty, stopx, stopy)
        
        self.rooms.append(Room(startx, starty, stopx, stopy))
        
        return True
    
    def buildAt(self, startx, starty, stopx, stopy, enemies = True):
        for x in range(startx, stopx+1):
            for y in range(starty, stopy+1):
                if x == startx or y == starty or x == stopx or y == stopy:
                    self.table[y][x] = 2
                else:
                    if enemies:
                        if g.getCurrentLevel() < 5:
                            # Less enemies on the first 4 floors
                            if random.randint(0,60 - 5*g.getCurrentLevel()) == 0:
                                self.table[y][x] = 3
                            else:
                                self.table[y][x] = 1
                        else:
                            if random.randint(0,30) == 0:
                                self.table[y][x] = 3
                            else:
                                self.table[y][x] = 1
                    else:
                        self.table[y][x] = 1
    def addExtra(self, room, extra):
        if extra == 4:
            self.table[room.starty+2][room.startx+2] = extra
            return True
        if extra == 5:
            if room.stopx > room.startx+2 and room.stopy > room.starty + 2:
                self.table[room.starty+2][room.startx+1] = extra
                return True
        return False
    
    def makeRoomsRounder(self):
        # Introduces a graphic bug :(
        for room in self.rooms:
        
            if room.startx+3 < room.stopx and room.starty+3 < room.stopy:
                self.table[room.starty+1][room.startx+1] = 2
            #if room.startx+3 < room.stopx and room.starty+3 < room.stopy:
            #    self.table[room.starty+1][room.stopx-1] = 2
            
            #if room.startx+3 < room.stopx and room.starty+3 < room.stopy:
            #    self.table[room.stopy-1][room.startx+1] = 2
            #if room.startx+3 < room.stopx and room.starty+3 < room.stopy:
            #    self.table[room.stopy-1][room.stopx-1] = 2
            
    def shrinkMap(self):
        # Shrinks the map to its minimum required space
        startX = startY = max(self.maxx, self.maxy)
        stopX = stopY = 0
        for x in range(0, self.maxx):
            for y in range(0, self.maxy):
                if self.table[y][x] != 0:
                    if startX > x:
                        startX = x
                    if startY > y:
                        startY = y
                    if stopX < x:
                        stopX = x
                    if stopY < y:
                        stopY = y
        
        
        newTable = [ [ 0 for i in range(stopX-startX + 2) ] for j in range(stopY - startY + 2) ]
        
        if startX > 1 and startY > 1:
            for x in range(startX - 1, stopX + 1):
                for y in range(startY - 1, stopY + 1):
                    newTable[y-startY + 1][x-startX + 1] = self.table[y][x]
        
        self.table = newTable
                    
           
#apa = MapMaker()
#apa.generateMap(100,20, 30, 10, 10)
#apa.printmap()
